package com.emeraldparser.collect;

import java.util.AbstractCollection;
import java.util.Iterator;
import java.util.Set;

import com.google.common.base.Objects;
import com.google.common.collect.Multiset;
import com.google.common.primitives.Ints;

public abstract class ManyToOneTransformedMultiset<F, T> extends AbstractCollection<T> implements Multiset<T> {

	protected abstract Multiset<F> sourceMultiset();

	protected abstract T apply(F sourceElement);

	@Override
	public int count(Object element) {
		long count = 0;
		
		for (Entry<F> sourceEntry : sourceMultiset().entrySet()) {
			T targetElement = apply(sourceEntry.getElement());
			if (Objects.equal(element, targetElement)) {
				count += sourceEntry.getCount();
			}
		}
		
		return Ints.saturatedCast(count);
	}

	@Override
	public int add(T element, int occurrences) {
		// TODO Auto-generated method stub
		return 0;
	}

	@Override
	public int remove(Object element, int occurrences) {
		// TODO Auto-generated method stub
		return 0;
	}

	@Override
	public int setCount(T element, int count) {
		// TODO Auto-generated method stub
		return 0;
	}

	@Override
	public boolean setCount(T element, int oldCount, int newCount) {
		// TODO Auto-generated method stub
		return false;
	}

	@Override
	public Set<T> elementSet() {
		final Set<F> sourceElementSet = sourceMultiset().elementSet();
		
		return new ManyToOneTransformedSet<F, T>() {

			@Override
			public Set<F> sourceSet() {
				return sourceElementSet;
			}

			@Override
			public T apply(F sourceElement) {
				return ManyToOneTransformedMultiset.this.apply(sourceElement);
			}

		};
	}

	@Override
	public Set<Entry<T>> entrySet() {
		// TODO Auto-generated method stub
		final Set<T> elementSet = elementSet();
		
		return new OneToManyTransformedSet<T, Entry<T>>() {

			@Override
			public Set<T> sourceSet() {
				return elementSet;
			}

			@Override
			public Entry<T> apply(final T element) {
				return new AbstractMultisetEntry<T>() {

					@Override
					public T getElement() {
						return element;
					}

					@Override
					public int getCount() {
						return count(element);
					}

				};
			}

			@Override
			protected T unapply(Entry<T> entry) {
				return entry.getElement();
			}

		};
	}

	@Override
	public Iterator<T> iterator() {
		final Iterator<F> sourceIterator = sourceMultiset().iterator();
		
		return new Iterator<T>() {

			@Override
			public boolean hasNext() {
				return sourceIterator.hasNext();
			}

			@Override
			public T next() {
				return apply(sourceIterator.next());
			}

			@Override
			public void remove() {
				sourceIterator.remove();
			}
			
		};
	}

	@Override
	public int size() {
		return sourceMultiset().size();
	}

}
